﻿using Furion;
using Furion.DatabaseAccessor;
using Furion.DataEncryption;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.EventBus;
using Furion.FriendlyException;
using Lazy.Captcha.Core;
using LimaLims.Application.Service.Role;
using LimaLims.Application.Service.Sys;
using LimaLims.Core;
using LimaLims.Core.Const;
using LimaLims.Core.Entity.System;
using LimaLims.Core.Enum;
using LimaLims.Core.Options;
using LimaLims.Core.Util;
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using UAParser;
using Yitter.IdGenerator;

namespace LimaLims.Application.Service
{
    /// <summary>
    /// 用户认证
    /// </summary>
    public class AuthService : IAuthService, IDynamicApiController, ITransient
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IRepository<SysUser> _sysUserRep; // 用户表仓储
        private readonly ISysRoleService _sysRoleService; // 系统角色服务
        private readonly ISysMenuService _sysMenuService; // 系统菜单服务
        private readonly ISysMessageService _sysMessageService;//系统消息
        private readonly IEventPublisher _eventPublisher;
        private readonly ICaptcha _captcha;//获取验证码
        private readonly IConfiguration _configuration;
        /// <summary>
        /// 构造函数
        /// </summary>
        public AuthService(IRepository<SysUser> sysUserRep, IHttpContextAccessor httpContextAccessor, ISysRoleService sysRoleService, ISysMenuService sysMenuService, ISysMessageService sysMessageService, IEventPublisher eventPublisher, ICaptcha captcha, IConfiguration configuration)
        {
            _sysUserRep = sysUserRep;
            _httpContextAccessor = httpContextAccessor;
            _sysRoleService = sysRoleService;
            _sysMenuService = sysMenuService;
            _sysMessageService = sysMessageService;
            _eventPublisher = eventPublisher;
            _captcha = captcha;
            _configuration = configuration;
        }

        /// <summary>
        /// 用户登录
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost("login")]
        [AllowAnonymous]
        public object LoginAsync([FromBody] LoginInput input)
        {
            //// 判断验证码
            //if (!_captcha.Validate(input.CodeId.ToString(), input.Code))
            //    throw Oops.Oh("验证码不正确");

            // 获取加密后的密码
            var encryptPasswod = MD5Encryption.Encrypt(input.Password);
            // 判断用户名和密码是否正确 忽略全局过滤器
            var user = _sysUserRep
                .Where(u => u.Account.Equals(input.Account) && u.Password.Equals(encryptPasswod), false, true)
                .FirstOrDefault();
            _ = user ?? throw Oops.Oh("用户名或密码不正确");

            // 验证账号是否有效
            if (!user.ValidStatus)
                throw Oops.Oh("账号被禁用，请联系管理员！");

            var tokenExpire = App.GetOptions<TokenSettingOptions>().ExpiredTime;

            // 生成Token令牌
            var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>
            {
                {ClaimConst.CLAINM_USERID, user.Id},
                {ClaimConst.TENANT_ID, user.TenantId},
                {ClaimConst.CLAINM_ACCOUNT, user.Account},
                {ClaimConst.CLAINM_NAME, user.Name},
                {ClaimConst.CLAINM_ADMINTYPE, user.AdminType}
            }, tokenExpire);

            // 设置Swagger自动登录
            _httpContextAccessor.HttpContext.SigninToSwagger(accessToken);

            var refreshTokenExpire = App.GetOptions<RefreshTokenSettingOptions>().ExpiredTime;
            // 生成刷新Token令牌
            var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, refreshTokenExpire);

            // 设置响应报文头
            _httpContextAccessor.HttpContext.SetTokensOfResponseHeaders(accessToken, refreshToken);

            return new
            {
                AccessToken = accessToken,
                RefreshToken = refreshToken,
            };
        }

        /// <summary>
        /// 获取刷新Token
        /// </summary>
        /// <param name="accessToken"></param>
        /// <returns></returns>
        [HttpGet("GetRefreshToken")]
        public string GetRefreshToken([FromQuery] string accessToken)
        {
            var refreshTokenExpire = App.GetOptions<RefreshTokenSettingOptions>().ExpiredTime;
            return JWTEncryption.GenerateRefreshToken(accessToken, refreshTokenExpire);
        }

        /// <summary>
        /// 获取当前登录用户信息
        /// </summary>
        /// <returns></returns>
        [HttpGet("getLoginUser")]
        public async Task<LoginOutput> GetLoginUserAsync()
        {
            //var msg = CheckRegisterCode();

            var user = _sysUserRep.FirstOrDefault(u => u.Id == CurrentUserInfo.UserId, false);
            if (user == null)
                throw Oops.Oh("不存在此用户信息！");
            var userId = user.Id;

            var httpContext = _httpContextAccessor.HttpContext;
            var loginOutput = user.Adapt<LoginOutput>();

            loginOutput.LastLoginTime = user.LastLoginTime = DateTime.Now;
            loginOutput.LastLoginIp = user.LastLoginIp = httpContext.GetRequestIPv4();

            var client = Parser.GetDefault().Parse(httpContext.Request.Headers["User-Agent"]);
            loginOutput.LastLoginBrowser = client.UA.Family + client.UA.Major;
            loginOutput.LastLoginOs = client.OS.Family + client.OS.Major;

            // 角色信息
            loginOutput.Roles = await _sysRoleService.GetUserRoleList(userId);

            // 权限信息
            loginOutput.Permissions = await _sysMenuService.GetLoginPermissionList(userId);

            // 系统所有权限信息
            loginOutput.AllPermissions = await _sysMenuService.GetAllPermissionList();

            //获取系统菜单
            loginOutput.Menus = await _sysMenuService.GetLoginMenusElement(userId);

            loginOutput.MessageCount = await _sysMessageService.GetUserMessageCount();

            //用户所属班组
            loginOutput.TeamId= user.TeamId;

            // 更新用户最后登录Ip和时间
            await _sysUserRep.UpdateIncludeAsync(user, new[] { nameof(SysUser.LastLoginIp), nameof(SysUser.LastLoginTime) });

            //增加登录日志
            await _eventPublisher.PublishAsync(new ChannelEventSource("Create:VisLog", new SysLogVis
            {
                Name = loginOutput.Name,
                Success = YesOrNot.Y,
                Message = "登录成功",
                Ip = loginOutput.LastLoginIp,
                Browser = loginOutput.LastLoginBrowser,
                Os = loginOutput.LastLoginOs,
                VisType = LoginType.LOGIN,
                VisTime = loginOutput.LastLoginTime,
                Account = loginOutput.Account
            }));
            return loginOutput;
        }

        /// <summary>
        /// 退出
        /// </summary>
        /// <returns></returns>
        [HttpPost("logout")]
        [AllowAnonymous]
        public async Task LogoutAsync()
        {
            if (string.IsNullOrWhiteSpace(CurrentUserInfo.Account))
                throw Oops.Oh("非法操作，未登录");

            var ip = _httpContextAccessor.HttpContext.GetRequestIPv4();
            _httpContextAccessor.HttpContext.SignoutToSwagger();

            //增加退出日志
            await _eventPublisher.PublishAsync(new ChannelEventSource("Create:VisLog",
                new SysLogVis
                {
                    Name = CurrentUserInfo.Name,
                    Success = YesOrNot.Y,
                    Message = "退出成功",
                    VisType = LoginType.LOGOUT,
                    VisTime = DateTime.Now,
                    Account = CurrentUserInfo.Account,
                    Ip = ip
                }));
        }

        /// <summary>
        /// 获取验证码
        /// </summary>
        /// <returns></returns>
        [HttpGet("GetCaptcha")]
        [AllowAnonymous]
        public dynamic GetCaptcha()
        {
            var codeNo = YitIdHelper.NextId().ToString();
            var captcha = _captcha.Generate(codeNo);

            return new { Id = codeNo, Img = captcha.Base64 };
        }


        /// <summary>
        /// 获取机器码
        /// </summary>
        /// <returns></returns>
        [HttpGet("GetMachineCode")]
        [AllowAnonymous]
        public string GetMachineCode()
        {
            return RegInfo.GetMachineCode();
        }

        [NonAction]
        public string CheckRegisterCode()
        {
            string appMachineCode = _configuration[$"AuthRegister:MachineCode"];
            string registerCode = _configuration[$"AuthRegister:RegisterCode"];

            // var machineCode = _configuration.GetSection("AuthRegister:MachineCode").Get<IEnumerable<string>>();
            //var registerCode = _configuration.GetSection("AuthRegister:RegisterCode").Get<IEnumerable<string>>();

            if (string.IsNullOrWhiteSpace(appMachineCode) || string.IsNullOrWhiteSpace(registerCode))
            {
                throw Oops.Oh("该商户未授权，请检查！");
            }

            DateTime overTime = DateTime.Now; DateTime registerTime = DateTime.Now; string machineCode = "";
            //检查注册码
            var checkRes = RegInfo.CheckRegister(registerCode, ref overTime, ref registerTime,ref machineCode);

            if (!appMachineCode.Equals(machineCode))
            {
                throw Oops.Oh("注册码错误（不是本机注册码）");
            }

            if (checkRes)
            {
                //获取当前时间（如果有网络则读取网络时间，否则获取本机时间）
                DateTime nowTime = MachineCodeUtil.GetDateTimeNow();
                //校验注册码注册时间
                var resDay = MachineCodeUtil.DiffDays(nowTime, registerTime);
                if (resDay > 0)
                {
                    throw Oops.Oh("注册码错误（当前时间小于注册时间，可能断网修改电脑时间导致）");
                }
                //校验注册码有效时间
                var resDay1 = MachineCodeUtil.DiffDays(nowTime, overTime);

                if (resDay1 <= 0)
                {
                    throw Oops.Oh("注册码错误（当前时间大于注册码有效时间，注册码过期）");
                }
                else
                {
                    return $"注册码有效,至{overTime}";
                }
            }
            else
            {
                throw Oops.Oh("注册码错误（不是本机注册码）");
            }

        }

    }
}
